package org.xxpay.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.xxpay.core.common.constant.Constant;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.domain.api.msg.QueryRetMsg;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.*;
import org.xxpay.core.entity.*;
import org.xxpay.core.service.IXxPayAgentpayService;
import org.xxpay.pay.channel.TransInterface;
import org.xxpay.pay.mq.BaseNotify4MchAgentpay;
import org.xxpay.pay.mq.BaseNotify4MchTrans;
import org.xxpay.pay.mq.Mq4AgentpayMchConfirm;
import org.xxpay.pay.mq.queue.MqQueue4Statistics;
import org.xxpay.pay.service.CommonService;
import org.xxpay.pay.util.SpringUtil;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author: dingzhiwei
 * @date: 18/10/02
 * @description: 代付业务
 */
@Service(interfaceName = "org.xxpay.core.service.IXxPayAgentpayService", version = Constant.XXPAY_SERVICE_VERSION, retries = Constant.DUBBO_RETRIES)
public class XxPayAgentpayServiceImpl implements IXxPayAgentpayService {

    private static final MyLog _log = MyLog.getLog(XxPayAgentpayServiceImpl.class);

    @Autowired
    private CommonService commonService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    public BaseNotify4MchAgentpay baseNotify4MchAgentpay;

    @Autowired
    public BaseNotify4MchTrans baseNotify4MchTrans;

    @Autowired
    private Mq4AgentpayMchConfirm mq4AgentpayMchConfirm;

    @Autowired
    private MqQueue4Statistics mqQueue4Statistics;

    private static final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void apply(MchAgentpayRecord record){
        apply(Arrays.asList(record));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void apply(List<MchAgentpayRecord> recordList) {

        if(recordList == null || recordList.isEmpty()) return ;
        if((recordList.size() > 1000))  throw new ServiceException(RetEnum.RET_MCH_SETT_BATCH_APPLY_LIMIT, "最多1000条");

        //获取商户ID
        Long mchId = recordList.get(0).getMchId();

        MchInfo mchInfo = commonService.mchInfoService.findByMchId(mchId); //获取商户信息

        ///判断当前 商户代付资金来源
        String subAmountFromStr = commonService.sysConfigService.getValByCode(MchConstant.AGENTPAY_OUT_ACCOUNT_CONFIG_KEY);
        if(StringUtils.isEmpty(subAmountFromStr)) throw new ServiceException(RetEnum.RET_MCH_CONFIG_NOT_EXIST);

        //验证资金来源是否配置正确
        byte subAmountFrom = Byte.parseByte(subAmountFromStr.trim());
        if(subAmountFrom != MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE && subAmountFrom != MchConstant.AGENTPAY_OUT_ACCOUNT_AGBALANCE){
            throw new ServiceException(RetEnum.RET_MCH_CONFIG_NOT_EXIST);
        }

        // 得到商户配置的代付通道
        List<MchAgentpayPassage> mchAgentpayPassageList = commonService.mchAgentpayPassageService.selectAvailable(mchId);
        if(CollectionUtils.isEmpty(mchAgentpayPassageList)) {
            throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_PASSAGE_NOT_EXIST);
        }

        MchAgentpayPassage mchPassage = mchAgentpayPassageList.get(0); //商户代付通道， 取第一条记录值
        Integer passageId = mchPassage.getAgentpayPassageId();  //代付通道ID
        AgentpayPassage passage = commonService.agentpayPassageService.findById(passageId); //代付通道对象

        // 获取代付通道子账户
        AgentpayPassageAccount passageAccount = null;  //代付子账号
        Integer passageAccountId = mchPassage.getAgentpayPassageAccountId();  //代付子账号ID
        if(passageAccountId != null && passageAccountId > 0) {  //passageAccountId 不为空说明配置为指定子账号， 否则需要根据金额去轮询可用子账号
            passageAccount = commonService.agentpayPassageAccountService.findById(passageAccountId);
        }

        Long maxEveryAmount = mchPassage.getMaxEveryAmount();  //单笔限额最大
        Long minEveryAmount = mchPassage.getMinEveryAmount();  //单笔限额最小
        String batchNo = MySeq.getUUID();   // 代付批次号
        Long totalAmount = 0L, totalFee = 0l;  // 代付总笔数  代付总金额  代付总手续费
        for(MchAgentpayRecord record : recordList) {

            Long applyAmount = record.getAmount(); //申请的代付金额
            if(applyAmount <= 0) throw new ServiceException(RetEnum.RET_SERVICE_AMOUNT_ERROR, "代付金额必须大于0");

            // 判断单笔限额最大
            if(maxEveryAmount != null && maxEveryAmount >= 0 &&  applyAmount > maxEveryAmount) {
                throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_AMOUNT_MAX_LIMIT, "单笔限额最大" + maxEveryAmount/100 + "元");
            }
            // 判断单笔限额最小
            if(minEveryAmount != null && minEveryAmount >= 0 &&  applyAmount < minEveryAmount) {
                throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_AMOUNT_MIN_LIMIT, "单笔限额最小" + minEveryAmount/100 + "元");
            }

            record.setStatus(PayConstant.AGENTPAY_STATUS_ING); // 代付中状态
            record.setAgentpayOrderId(MySeq.getAgentpay()); // 生成代付订单号
            record.setBatchNo(batchNo);  //代付批次号
            record.setSubAmountFrom(subAmountFrom); //商户扣减资金来源

            // 单笔代付手续费
            record.setFee(
                    XXPayUtil.getFee(mchPassage.getMchFeeType(), mchPassage.getMchFeeRate(), mchPassage.getMchFeeEvery(), applyAmount)
            );
            record.setRemitAmount(applyAmount);      // 打款金额 与 申请金额一致
            record.setSubAmount(applyAmount + record.getFee());  // 扣减账户金额 = 代付金额 + 手续费
            if(StringUtils.isBlank(record.getRemark())) record.setRemark("代付:" + applyAmount/100 + "元");  //备注信息
            record.setPassageId(passageId); //商户通道ID
            record.setConfirmPassageId(passageId); //商户通道ID

            AgentpayPassageAccount canUsePassageAccount = null;  //函数内的子账号

            if(passageAccount != null){  //配置为指定子账号

                AgentpayPassageAccount checkResult = checkAndGetAccountCanUse("", passage, passageAccount, applyAmount);

                if(checkResult != null){ //账号可用
                    canUsePassageAccount = checkResult;
                }
            }else{ //需要遍历子账号进行查找可用子账号
                canUsePassageAccount = getAvailablePassageAccount("", passage, applyAmount);
            }

            if(canUsePassageAccount == null) { // 判断是否取到可用子账户
                throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_PASSAGE_NOT_EXIST, "该代付通道没有可用子账户[agentpayPassageId="+passageId+"]");
            }

            record.setPassageAccountId(canUsePassageAccount.getId());
            record.setConfirmPassageAccountId(canUsePassageAccount.getId());

            //API接口形式 && 需要二次确认
            if(record.getAgentpayChannel() == MchConstant.AGENTPAY_CHANNEL_API && mchInfo.getAgentpayAPIConfirmFlag() == MchConstant.PUB_YES){

                record.setStatus(PayConstant.AGENTPAY_STATUS_WAIT_API_CONFIRM); // 等待接口二次确认

                //封装二次确认地址
                String apiConfirmJson = mchInfo.getAgentpayAPIConfirmJson();
                // 二次确认参数配置不可为空
                if (StringUtils.isEmpty(apiConfirmJson)) return;

                JSONObject confirmJson = JsonUtil.getJSONObjectFromJson(apiConfirmJson);
                Map<String, Object> paraMap = new HashMap<>();
                paraMap.put(confirmJson.getString("mchOrderNo"), record.getMchOrderNo());
                paraMap.put(confirmJson.getString("amount"), record.getAmount());
                String urlStr = XXPayUtil.genUrlParams(paraMap);
                String url = mchInfo.getAgentpayAPIConfirmUrl().indexOf("?") >= 0 ?
                        mchInfo.getAgentpayAPIConfirmUrl() + "&" + urlStr:
                        mchInfo.getAgentpayAPIConfirmUrl() + "?" + urlStr;

                record.setMchConfirmReqUrl(url); //二次确认地址

            }else if(   //判断是否需要人工处理  [ 全部需要人工处理 || 转卡通道的形式  ]
                    MchConstant.AGENTPAY_AUDIT_TYPE_AUDIT == Byte.parseByte(commonService.sysConfigService.getValByCode("agentpayAuditType"))
            ){

                record.setStatus(PayConstant.AGENTPAY_STATUS_WAIT_AUDIT); // 等待人工处理

            }else{ //自动打款

                //填充 TransOrder对象信息
                TransOrder transOrder = new TransOrder();
                transOrder.setChannelType(canUsePassageAccount.getIfTypeCode());
                transOrder.setChannelId(canUsePassageAccount.getIfCode());
                transOrder.setChannelMchId(canUsePassageAccount.getPassageMchId());
                resetTransOrderId(transOrder); //设置transOrderId
                transOrder.setStatus(PayConstant.TRANS_STATUS_TRANING); //转账中
                transOrder.setMchId(record.getMchId());
                transOrder.setMchType(record.getMchType());
                transOrder.setMchTransNo(record.getAgentpayOrderId());
                transOrder.setPassageId(canUsePassageAccount.getAgentpayPassageId());
                transOrder.setPassageAccountId(canUsePassageAccount.getId());
                transOrder.setAmount(record.getAmount());
                transOrder.setCurrency("cny");
                transOrder.setClientIp(IPUtility.getLocalIP());
                transOrder.setDevice("WEB");
                transOrder.setRemarkInfo(record.getRemark());
                transOrder.setBankName(record.getBankName());
                transOrder.setAccountName(record.getAccountName());
                transOrder.setAccountNo(record.getAccountNo());

                //TODO 保存的渠道成本为 商户成本？？？  q: 2020-08-19
                if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE) {   // 百分比收费
                    transOrder.setChannelRate(mchPassage.getMchFeeRate());
                    transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()));
                }else if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_FIX) { // 固定收费

                    transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
                    transOrder.setChannelCost(mchPassage.getMchFeeEvery());

                }else if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE_AND_FIX) { // 百分比收费 + 固定收费

                    transOrder.setChannelRate(mchPassage.getMchFeeRate());
                    transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
                    transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()) + mchPassage.getMchFeeEvery());
                }

                record.setTransOrderId(transOrder.getTransOrderId());
                record.setPsVal("transOrder", transOrder);
            }

            //每笔结果进行累加
            totalAmount += applyAmount; totalFee += record.getFee();
        }

        //验证该代付银行卡列表是否 包含重复，并且是否频繁操作
        this.checkAccountCache(recordList);

        //冻结商户资金 & agentPayRecord + transOrder持久化操作
        commonService.mchAgentpayService.frozenAmountAndInsertRecord(totalAmount + totalFee, recordList);

        //发送代付下单统计通知
        mqQueue4Statistics.sendAgentPayStatisticsBatch(recordList, MchConstant.ORDER_STATUS_INIT, true);

        //将银行账号放入缓存
        this.setAccountCache(recordList);

        //发送二次确认MQ
        recordList.stream().forEach(record -> {
            if(record.getStatus() == PayConstant.AGENTPAY_STATUS_WAIT_API_CONFIRM){
                //务必延迟发送确认请求， 否则有可能商户订单未落库导致查询不到， 导致相互失败。
                mq4AgentpayMchConfirm.send(record.getAgentpayOrderId(), mchInfo.getAgentpayAPIConfirmJson(), 1, 10 * 1000);
            }
        });

        if(recordList.size() == 1){ //只包含1个代付订单，则同步处理

            TransOrder transOrder = recordList.get(0).getPsVal("transOrder", TransOrder.class);

            if(transOrder != null){
                executeChannelAPI(transOrder);
            }

            return ;
        }

        //执行异步线程处理
        scheduledThreadPool.execute(() ->
            recordList.stream().forEach(record -> {
                TransOrder transOrder = record.getPsVal("transOrder", TransOrder.class);
                if(transOrder != null){
                    executeChannelAPI(transOrder);
                }
            })
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String applyBySett(TransOrder transOrder){

        resetTransOrderId(transOrder); //设置transOrderId
        transOrder.setStatus(PayConstant.TRANS_STATUS_TRANING); //转账中
        commonService.transOrderService.createTransOrder(transOrder); //入库操作
        executeChannelAPI(transOrder); //调起上游接口

        return transOrder.getTransOrderId();

    }

    /** 根据具体通道重新设置transOrderId **/
    private void resetTransOrderId(TransOrder transOrder){

        try{
            TransInterface transInterface = (TransInterface) SpringUtil.getBean(transOrder.getChannelType() + "TransService");
            transOrder.setTransOrderId(transInterface.getTransOrderId(transOrder)); //重新设置transOrderId
        }catch (BeansException e) {
            _log.error(e, "转账渠道类型[channelType="+transOrder.getChannelType()+"]实例化异常");
            throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);
        }
    }


    /** 验证该子账号是否可用 **/
    private AgentpayPassageAccount getAvailablePassageAccount(String riskLog, AgentpayPassage passage, Long amount) {
        // 判断代付通道
        if(passage == null || passage.getStatus() != MchConstant.PUB_YES) {
            throw new ServiceException(RetEnum.RET_MGR_AGENTPAY_PASSAGE_NOT_EXIST, "代付通道不存在或已关闭[agentpayPassageId="+passage.getId()+"]");
        }
        // 得到可用的支付通道子账户 && 随机打乱子账户顺序
        List<AgentpayPassageAccount> passageAccountList = commonService.agentpayPassageAccountService.selectAllByPassageId(passage.getId());
        Collections.shuffle(passageAccountList);

        for(AgentpayPassageAccount passageAccount : passageAccountList) {
            AgentpayPassageAccount thisItem = checkAndGetAccountCanUse(riskLog, passage,  passageAccount, amount);
            if(thisItem != null) return thisItem;
        }

        return null;
    }

    /** 遍历子账号， 查找可用子账号 **/
    private AgentpayPassageAccount checkAndGetAccountCanUse(String riskLog, AgentpayPassage passage, AgentpayPassageAccount passageAccount, Long amount) {

        // 判断账号状态 是否为关闭
        if(passage.getStatus() != MchConstant.PUB_YES || passageAccount.getStatus() != MchConstant.PUB_YES ) {
            return null;
        }

        // 风控继承模式,设置继承的风控信息
        if(passageAccount.getRiskMode() == 1) {
            passageAccount.setRiskStatus(passage.getRiskStatus());
            passageAccount.setMaxDayAmount(passage.getMaxDayAmount());
            passageAccount.setTradeStartTime(passage.getTradeStartTime());
            passageAccount.setTradeEndTime(passage.getTradeEndTime());
        }

        // 判断风控状态为关闭
        if(passageAccount.getRiskStatus() == MchConstant.PUB_NO) {
            return null;
        }

        // 下面逻辑判断是否触发风控规则,如果触发则返回空
        // 交易时间是否满足
        String ymd = DateUtil.date2Str(new Date(), DateUtil.FORMAT_YYYY_MM_DD);
        String tradeStartTime = ymd + " " + passageAccount.getTradeStartTime();
        String tradeEndTime = ymd + " " + passageAccount.getTradeEndTime();
        long startTime = DateUtil.str2date(tradeStartTime).getTime();
        long endTime = DateUtil.str2date(tradeEndTime).getTime();
        long currentTime = System.currentTimeMillis();
        if(currentTime < startTime || currentTime > endTime) {
            _log.info("{}交易时间触发风控.agetnpayPassageAccountId={},tradeStartTime={},tradeEndTime={}", riskLog, passageAccount.getId(), tradeStartTime, tradeEndTime);
            return null;
        }
        // 今日总交易金额是否满足
        long maxDayAmount = passageAccount.getMaxDayAmount();
        long dayAmount = commonService.transOrderService.sumAmount4AgentpayPassageAccount(passageAccount.getId(),
                DateUtil.str2date(ymd + " 00:00:00"),
                DateUtil.str2date(ymd + " 23:59:59"));
        if(dayAmount > maxDayAmount) {
            _log.info("{}每日交易总额触发风控.agentpayPassageAccountId={},maxDayAmount={},dayAmount={}", riskLog, passageAccount.getId(), maxDayAmount, dayAmount);
            return null;
        }

        return passageAccount;
    }


    /** 验证该代付银行卡列表是否 包含重复，并且是否频繁操作 **/
    private void checkAccountCache(List<MchAgentpayRecord> recordList){

        // 查看是否有重复代付申请记录
        Set<String> set = new HashSet<>();
        for(MchAgentpayRecord record : recordList) {
            String cacheKey = XXPayUtil.genAgentpayCacheKey(record);
            if(set.contains(cacheKey)) {
                throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_ACCOUNTNO_REPEAT, "重复申请,卡号:" + record.getAccountNo() + ",金额:" + record.getAmount()/100.0 + "元");
            }

            if(commonService.sysConfigService.getDBAgentpayConfig().getAgentpayCheckRepeatFlag() == MchConstant.PUB_YES){
                // 判断redis中是否有该笔转账
                if(stringRedisTemplate.hasKey(cacheKey)) {
                    throw new ServiceException(RetEnum.RET_MCH_AGENTPAY_INTERVAL_SHORT, "频繁代付,卡号:" + record.getAccountNo() + ",金额:" + record.getAmount()/100.0 + "元");
                }
            }

            set.add(cacheKey);
        }
        set = null; //GC回收
    }

    /** 验证该代付银行卡列表是否 包含重复，并且是否频繁操作 **/
    private void setAccountCache(List<MchAgentpayRecord> recordList){
        // 将每个申请设置到redis
        for(MchAgentpayRecord record : recordList) {

            if(commonService.sysConfigService.getDBAgentpayConfig().getAgentpayCheckRepeatFlag() == MchConstant.PUB_YES){

                // 向redis里存入数据并设置超时时间
                stringRedisTemplate.opsForValue().set(XXPayUtil.genAgentpayCacheKey(record),
                        record.getAccountNo(), commonService.sysConfigService.getDBAgentpayConfig().getAgentpayCheckRepeatTime(), TimeUnit.SECONDS);
            }

        }
    }



    @Override
    public void executeChannelAPI(TransOrder transOrder) {

        String channelType = transOrder.getChannelType(); //接口类型
        QueryRetMsg retMsg ;

        try {
            //获取对应的接口 & 调起上游转账接口
            TransInterface transInterface = SpringUtil.getBean(channelType + "TransService", TransInterface.class);
            retMsg = transInterface.trans(transOrder); //接口返回信息

        } catch (BeansException e) { //实例化异常
            _log.error(e, "转账渠道类型[channelType="+channelType+"]实例化异常");
            retMsg = QueryRetMsg.confirmFail();  //接口实例化失败, 没有调起上游接口, 直接修改为失败！

        }catch (Exception e) {
            _log.error("exception", e);
            retMsg = QueryRetMsg.sysError(); //系统异常， 判断是否调起上游成功, 或者哪一步出现的问题，需要等待进行查询补单操作！
        }

        //处理状态
        processOrderState(transOrder, retMsg);

    }

    @Override
    public boolean queryChannelAndProcessOrderState(TransOrder transOrder) {

        String transOrderId = transOrder.getTransOrderId();
        String channelType = transOrder.getChannelType(); //接口类型

        try{
            //1. 调起上游查询接口
            TransInterface transInterface = (TransInterface) SpringUtil.getBean(channelType + "TransService");
            QueryRetMsg retObj = transInterface.query(transOrder);

            //2. 处理订单状态
            return processOrderState(transOrder, retObj);

        }catch (BeansException e) {
            _log.warn("不支持的转账渠道,停止处理.transOrderId={},channelType={}", transOrderId, channelType);
            return false;
        }catch (Exception e) {
            _log.warn("转账异常,停止处理.transOrderId={},channelType={}", transOrderId, channelType);
            return false;
        }
    }

    @Override
    public boolean processOrderState(TransOrder transOrder, QueryRetMsg retMsg){
        return processOrderState(transOrder, retMsg, true);  //兼容以前的程序，全部支出通知下游订单状态
    }

    public boolean processOrderState(TransOrder transOrder, QueryRetMsg retMsg, boolean isNotifyMch){

        String transOrderId = transOrder.getTransOrderId();

        try {
            if(retMsg == null) {
                _log.error("处理转账订单状态, retMsg is null");
                return false;
            }

            if( retMsg.getChannelState() == QueryRetMsg.ChannelState.WAITING ||
                retMsg.getChannelState() == QueryRetMsg.ChannelState.UNKNOWN ||
                retMsg.getChannelState() == QueryRetMsg.ChannelState.SYS_ERROR ) { //处理中, 状态未知, 系统异常，暂不处理
                return true;
            }


            MchAgentpayRecord record = commonService.mchAgentpayService.findByTransOrderId(transOrderId); //查询agentPayOrder

            if(retMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_SUCCESS){ //明确转账成功
                transOrder.setStatus(PayConstant.TRANS_STATUS_SUCCESS); //用于商户通知

                if(record == null) { //仅更新transOrder
                    commonService.transOrderService.updateStatus4Success(transOrderId, retMsg.getChannelOrderId());
                }else{
                    // 更新 agentPayRecord + transOrder + 商户资金
                    commonService.mchAgentpayService.updateStatus4Success(record.getAgentpayOrderId(), transOrderId, retMsg.getChannelOrderId(), false);
                    record.setStatus(PayConstant.AGENTPAY_STATUS_SUCCESS); //用于商户通知

                    //发送代付成功统计通知
                    mqQueue4Statistics.sendAgentPayStatistics(record, MchConstant.ORDER_STATUS_SUCCESS);
                }
            }else if(retMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_FAIL) { //明确转账失败
                transOrder.setStatus(PayConstant.TRANS_STATUS_FAIL); //用于商户通知

                // 更新转账状态为失败
                String channelErrCode = retMsg.getChannelErrCode();
                String channelErrMsg = retMsg.getChannelErrMsg();
                if (record == null) { // 仅更新 [transOrder]
                    commonService.transOrderService.updateStatus4Fail(transOrderId, channelErrCode, channelErrMsg);
                } else {
                    // 更新 agentPayRecord + transOrder + 商户资金
                    commonService.mchAgentpayService.updateStatus4Fail(record.getAgentpayOrderId(), transOrderId, channelErrCode, channelErrMsg, PayConstant.AGENTPAY_STATUS_ING);
                    record.setStatus(PayConstant.AGENTPAY_STATUS_FAIL); //用于商户通知
                }
            }

            baseNotify4MchTrans.doNotify(transOrder, true); //通知下游 （只有 运营平台的[打款+转账]功能会存在 notifyUrl地址  ）

            if(record != null) {  //删除转账账号缓存 && 通知 [agentpayRecord] 的 notifyUrl
                stringRedisTemplate.delete(XXPayUtil.genAgentpayCacheKey(record)); //删除缓存时间
                if(isNotifyMch){
                    baseNotify4MchAgentpay.doNotify(record, true); //通知下游
                }
            }

            return true;
        } catch (Exception e) {
            _log.error("处理代付订单状态异常， transOrderId = {}, retMsg = {}", transOrderId, retMsg,  e);
            return false;
        }
    }



    /**  人工审核代付成功 后 更新支付成功 and 通知商户 (提取公共code)
     *  1. 选择多张银行卡
     *  2. 选择其他
     *  3. 选择支付通道后选择了一张银行卡
     *
     * **/
    private void auditOK4UpdateSuccessAndNotifyMch(MchAgentpayRecord record){

        //先更新基础信息
        MchAgentpayRecord updateRecord = new MchAgentpayRecord();
        updateRecord.setAgentpayOrderId(record.getAgentpayOrderId());
        updateRecord.setConfirmPassageId(record.getConfirmPassageId()); //更新代付通道 account ID
        updateRecord.setConfirmPassageAccountId(record.getConfirmPassageAccountId()); //更新代付通道ID
        updateRecord.setTccardAccountNo(record.getTccardAccountNo()); //转卡银行卡号
        updateRecord.setUpdateTime(new Date()); //避免上述没有数据导致更新异常
        updateRecord.setOperatorUserId(record.getOperatorUserId());
        updateRecord.setOperatorName(record.getOperatorName());
        commonService.mchAgentpayService.updateById(record);

        // 更新 agentPayRecord + transOrder + 商户资金
        commonService.mchAgentpayService.updateStatus4Success(record.getAgentpayOrderId(), null, null, true);
        record.setStatus(PayConstant.AGENTPAY_STATUS_SUCCESS); //用于商户通知

        //发送代付成功统计通知
        mqQueue4Statistics.sendAgentPayStatistics(record, MchConstant.ORDER_STATUS_SUCCESS);

        stringRedisTemplate.delete(XXPayUtil.genAgentpayCacheKey(record)); //删除缓存时间
        baseNotify4MchAgentpay.doNotify(record, true); //通知下游
    }


    @Override
    @Transactional
    public void auditOk(MchAgentpayRecord record, Integer choosePayType){

        if(choosePayType == 2){ //多张银行卡

            record.setConfirmPassageId(-1);
            record.setConfirmPassageAccountId(-1); //默认无需处理

            //通过fastjson处理map , dubbo可能转换有问题。 比如：Byte转换为Int
            JSONObject psJSON = new JSONObject(record.getPs() == null ? new HashMap<>(): record.getPs());
            Byte calProfitRuleType = psJSON.getByte("calProfitRuleType");
            if(calProfitRuleType != null && calProfitRuleType == MchConstant.PUB_YES){ //计算分润
                record.setConfirmPassageId(psJSON.getInteger("calProfitRuleTypeAgentpayPassageId"));
                record.setConfirmPassageAccountId(psJSON.getInteger("calProfitRuleTypeAgentpayPassageAccountId"));
            }

            //更新成功 and 通知商户
            auditOK4UpdateSuccessAndNotifyMch(record);
            return;

        }else if(choosePayType == 3){ //直接更改为成功状态

            record.setConfirmPassageId(-1);
            record.setConfirmPassageAccountId(-1); //默认无需处理

            //通过fastjson处理map , dubbo可能转换有问题。 比如：Byte转换为Int
            JSONObject psJSON = new JSONObject(record.getPs() == null ? new HashMap<>(): record.getPs());
            Byte calProfitRuleType = psJSON.getByte("calProfitRuleType");
            if(calProfitRuleType != null && calProfitRuleType == MchConstant.PUB_YES){ //计算分润
                record.setConfirmPassageId(psJSON.getInteger("calProfitRuleTypeAgentpayPassageId"));
                record.setConfirmPassageAccountId(psJSON.getInteger("calProfitRuleTypeAgentpayPassageAccountId"));
            }

            //更新成功 and 通知商户
            auditOK4UpdateSuccessAndNotifyMch(record);
            return;

        }else if(choosePayType == 1) { //选择一种银行卡 、 或者通道类型

            //1. 验证当前通道是否可用
            AgentpayPassageAccount canUsePassageAccount = commonService.agentpayPassageAccountService.findById(record.getConfirmPassageAccountId());
            AgentpayPassage passage = commonService.agentpayPassageService.findById(record.getConfirmPassageId());

            AgentpayPassageAccount checkResult = checkAndGetAccountCanUse("", passage, canUsePassageAccount, record.getRemitAmount());
            if (checkResult == null) {
                throw new ServiceException(RetEnum.RET_SERVICE_AGENTPAY_RISK_ERROR);
            }


            //以下为 接口形式
            // 2.
            //填充 TransOrder对象信息
            TransOrder transOrder = new TransOrder();
            transOrder.setChannelType(canUsePassageAccount.getIfTypeCode());
            transOrder.setChannelId(canUsePassageAccount.getIfCode());
            transOrder.setChannelMchId(canUsePassageAccount.getPassageMchId());
            resetTransOrderId(transOrder); //设置transOrderId
            transOrder.setStatus(PayConstant.TRANS_STATUS_TRANING); //转账中
            transOrder.setMchId(record.getMchId());
            transOrder.setMchType(record.getMchType());
            transOrder.setMchTransNo(record.getAgentpayOrderId());
            transOrder.setPassageId(canUsePassageAccount.getAgentpayPassageId());
            transOrder.setPassageAccountId(canUsePassageAccount.getId());
            transOrder.setAmount(record.getAmount());
            transOrder.setCurrency("cny");
            transOrder.setClientIp(IPUtility.getLocalIP());
            transOrder.setDevice("WEB");
            transOrder.setRemarkInfo(record.getRemark());
            transOrder.setAccountName(record.getAccountName());
            transOrder.setAccountNo(record.getAccountNo());

            MchAgentpayPassage mchPassage = commonService.mchAgentpayPassageService.findByMchIdAndAgentpayPassageId(record.getMchId(), record.getPassageId());

            //TODO 保存的渠道成本为 商户成本？？？  q: 2020-08-19
            if (mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE) {   // 百分比收费
                transOrder.setChannelRate(mchPassage.getMchFeeRate());
                transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()));
            } else if (mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_FIX) { // 固定收费

                transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
                transOrder.setChannelCost(mchPassage.getMchFeeEvery());

            } else if (mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE_AND_FIX) { // 百分比收费 + 固定收费

                transOrder.setChannelRate(mchPassage.getMchFeeRate());
                transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
                transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()) + mchPassage.getMchFeeEvery());
            }

            //transOrder 入库
            commonService.transOrderService.createTransOrder(transOrder);

            //record 更新为代付中，  更新transOrderId
            record.setTransOrderId(transOrder.getTransOrderId());
            record.setStatus(PayConstant.AGENTPAY_STATUS_ING);
            commonService.mchAgentpayService.updateAudit2Ing(record.getAgentpayOrderId(), record);

            //调用转账接口
            executeChannelAPI(transOrder);

            return;
        }

    }

    @Override
    @Transactional
    public void auditReject(MchAgentpayRecord record, Byte recordOldStatus){

        // 更新 agentPayRecord + transOrder + 商户资金
        commonService.mchAgentpayService.updateStatus4Fail(record.getAgentpayOrderId(), null, null, null, recordOldStatus);
        record.setStatus(PayConstant.AGENTPAY_STATUS_FAIL); //用于商户通知

        stringRedisTemplate.delete(XXPayUtil.genAgentpayCacheKey(record)); //删除缓存时间
        baseNotify4MchAgentpay.doNotify(record, true); //通知下游

    }

    /*** 二次确认之后继续执行之后的逻辑 **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void mchConfirmNext(String agentpayOrderId) {

        //判断是否
        MchAgentpayRecord record = commonService.mchAgentpayService.findByAgentpayOrderId(agentpayOrderId);

        if(  //判断是否需要人工处理  [ 全部需要人工处理 || 转卡通道的形式  ]
                MchConstant.AGENTPAY_AUDIT_TYPE_AUDIT == Byte.parseByte(commonService.sysConfigService.getValByCode("agentpayAuditType"))
                        || StringUtils.isNotEmpty(record.getTccardAccountNo())

        ) {

            //更新成 待审核  结束
            int updateRow = commonService.mchAgentpayService.updateStatus4WaitMchComfirmSuccess(agentpayOrderId, PayConstant.AGENTPAY_STATUS_WAIT_AUDIT, null);
            if(updateRow <= 0){
                throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);
            }

            return ;
        }

        //以下为： 自动打款 方式

        AgentpayPassageAccount canUsePassageAccount = commonService.agentpayPassageAccountService.findById(record.getPassageAccountId());

        MchAgentpayPassage mchPassage = commonService.mchAgentpayPassageService.findByMchIdAndAgentpayPassageId(record.getMchId(), record.getPassageId());

        //填充 TransOrder对象信息
        TransOrder transOrder = new TransOrder();
        transOrder.setChannelType(canUsePassageAccount.getIfTypeCode());
        transOrder.setChannelId(canUsePassageAccount.getIfCode());
        transOrder.setChannelMchId(canUsePassageAccount.getPassageMchId());
        resetTransOrderId(transOrder); //设置transOrderId
        transOrder.setStatus(PayConstant.TRANS_STATUS_TRANING); //转账中
        transOrder.setMchId(record.getMchId());
        transOrder.setMchType(record.getMchType());
        transOrder.setMchTransNo(record.getAgentpayOrderId());
        transOrder.setPassageId(canUsePassageAccount.getAgentpayPassageId());
        transOrder.setPassageAccountId(canUsePassageAccount.getId());
        transOrder.setAmount(record.getAmount());
        transOrder.setCurrency("cny");
        transOrder.setClientIp(IPUtility.getLocalIP());
        transOrder.setDevice("WEB");
        transOrder.setRemarkInfo(record.getRemark());
        transOrder.setBankName(record.getBankName());
        transOrder.setAccountName(record.getAccountName());
        transOrder.setAccountNo(record.getAccountNo());

        //TODO 保存的渠道成本为 商户成本？？？  q: 2020-08-19
        if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE) {   // 百分比收费
            transOrder.setChannelRate(mchPassage.getMchFeeRate());
            transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()));
        }else if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_FIX) { // 固定收费

            transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
            transOrder.setChannelCost(mchPassage.getMchFeeEvery());

        }else if(mchPassage.getMchFeeType() == MchConstant.FEE_TYPE_RATE_AND_FIX) { // 百分比收费 + 固定收费

            transOrder.setChannelRate(mchPassage.getMchFeeRate());
            transOrder.setChannelFeeEvery(mchPassage.getMchFeeEvery());
            transOrder.setChannelCost(XXPayUtil.calOrderMultiplyRate(record.getAmount(), mchPassage.getMchFeeRate()) + mchPassage.getMchFeeEvery());
        }

        commonService.mchAgentpayService.updateStatus4WaitMchComfirmSuccess(agentpayOrderId, PayConstant.AGENTPAY_STATUS_ING, transOrder);

        executeChannelAPI(transOrder);

    }


    /** 查询代付订单 **/
    public MchAgentpayRecord query(Long mchId, String agentpayOrderId, String mchOrderNo, boolean executeNotify) {
        MchAgentpayRecord mchAgentpayRecord;
        if(StringUtils.isNotBlank(agentpayOrderId)) {
            mchAgentpayRecord = commonService.mchAgentpayService.findByMchIdAndAgentpayOrderId(mchId, agentpayOrderId);
        }else {
            mchAgentpayRecord = commonService.mchAgentpayService.findByMchIdAndMchOrderNo(mchId, mchOrderNo);
        }
        if(mchAgentpayRecord == null) return null;

        if(executeNotify && (PayConstant.AGENTPAY_STATUS_SUCCESS == mchAgentpayRecord.getStatus() || PayConstant.AGENTPAY_STATUS_FAIL == mchAgentpayRecord.getStatus())) {
            baseNotify4MchAgentpay.doNotify(mchAgentpayRecord, false);
            _log.info("业务查单完成,并再次发送业务代付通知.发送完成");
        }
        return mchAgentpayRecord;
    }


    /** 代付补单 **/
    @Transactional
    @Override
    public void doReissue(String agentpayOrderId, QueryRetMsg queryRetMsg, boolean isNotifyMch) {

        MchAgentpayRecord record = commonService.mchAgentpayService.findByAgentpayOrderId(agentpayOrderId);
        if(record == null){
            throw new ServiceException(RetEnum.RET_COMM_RECORD_NOT_EXIST);
        }

        //待审核 此时 冻结商户资金， 并且 没有transOrder
        if(record.getStatus() == PayConstant.AGENTPAY_STATUS_INIT || record.getStatus() == PayConstant.AGENTPAY_STATUS_WAIT_AUDIT ){
            throw new ServiceException(RetEnum.RET_MGR_STATUS_ERROR);
        }

        //只考虑 1， 2， 3的情况
        Byte currentStatus = record.getStatus();
        if(currentStatus == PayConstant.AGENTPAY_STATUS_ING){ // [处理中] 状态

            TransOrder transOrder = commonService.transOrderService.findByTransOrderId(record.getTransOrderId());
            if(transOrder == null){
                throw new ServiceException(RetEnum.RET_MCH_TRADE_ORDER_NOT_EXIST);
            }

            boolean isTrue = this.processOrderState(transOrder, queryRetMsg, isNotifyMch); //按照回调方式进行处理
            if(!isTrue){
                throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);
            }
        }

        if(currentStatus == PayConstant.AGENTPAY_STATUS_SUCCESS){ // [成功] 状态

            if(queryRetMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_SUCCESS){  //改为[成功]状态 --无意义
            }else if(queryRetMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_FAIL){ //改为[失败]状态

                //代付成功会扣除商户代付资金， 此时应该给商户加钱
                String bizItem = MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE == record.getSubAmountFrom() ? MchConstant.BIZ_ITEM_BALANCE : MchConstant.BIZ_ITEM_AGENTPAY_BALANCE;
                commonService.mchAccountService.changeBalanceAndInsertHistoryByPlat(record.getMchId(), record.getAmount() + record.getFee(), bizItem, "补单:" + record.getAgentpayOrderId());

                //更新代付订单 + 转账订单状态 (成功--》 失败)
                int updateRow = commonService.mchAgentpayService.updateByReissue(agentpayOrderId, PayConstant.AGENTPAY_STATUS_FAIL);
                if(updateRow <= 0) throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);

                //由[成功]改为[失败]状态，相应统计应该减掉成功笔数和金额
                mqQueue4Statistics.sendAgentPayStatisticsFail(record);

                if(isNotifyMch){
                    baseNotify4MchAgentpay.doNotify(record, true); //通知下游
                }
            }
        }

        if(currentStatus == PayConstant.AGENTPAY_STATUS_FAIL){ // [失败] 状态

            if(queryRetMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_SUCCESS){  //改为[成功]状态

                //代付成功会扣除商户代付资金， 此时应该给商户减少资金
                String bizItem = MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE == record.getSubAmountFrom() ? MchConstant.BIZ_ITEM_BALANCE : MchConstant.BIZ_ITEM_AGENTPAY_BALANCE;
                commonService.mchAccountService.changeBalanceAndInsertHistoryByPlat(record.getMchId(), (0 - record.getAmount() - record.getFee()), bizItem, "补单:" + record.getAgentpayOrderId());

                //更新代付订单 + 转账订单状态 (失败--》 成功)
                int updateRow = commonService.mchAgentpayService.updateByReissue(agentpayOrderId, PayConstant.AGENTPAY_STATUS_SUCCESS);
                if(updateRow <= 0) throw new ServiceException(RetEnum.RET_COMM_OPERATION_FAIL);

                //发送代付成功统计通知
                mqQueue4Statistics.sendAgentPayStatistics(record, MchConstant.ORDER_STATUS_SUCCESS, false);

                if(isNotifyMch){
                    baseNotify4MchAgentpay.doNotify(record, true); //通知下游
                }

            }else if(queryRetMsg.getChannelState() == QueryRetMsg.ChannelState.CONFIRM_FAIL){ //改为[失败]状态  --无意义
            }
        }

    }

}
